﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Scripting;

namespace Nirvana
{
    /// <summary>
    /// Prefab资源加载引用计数缓存脚本
    /// </summary>
    public sealed class PrefabCache 
    {
        private static Logger logger = LogSystem.GetLogger("PrefabCache");

        private GameObject instancePrefab;
        private Dictionary<GameObject, PrefabCache> prefabCacheMap;
        private PoolStrategy poolstrategy;
        private float defaultReleaseAfterFree = 30f;
        private int defaultMaxPoolCount = 16;
        private bool isLoaded;
        private float lastFreeTime = -1f;
        [Preserve]
        public AssetID AssetID { get; private set; }
        [Preserve]
        public int ReferenceCount { get; private set; }
        [Preserve]
        public float LastFreeTime
        {
            get
            {
                return lastFreeTime;
            }
        }

        [Preserve]
        public float ReleaseAfterFree { get; private set; }
        public bool IsSync { get; set; }
        public string Error { get; private set; }

        public PrefabCache(AssetID assetid,Dictionary<GameObject,PrefabCache> datamap)
        {
            this.AssetID = assetid;
            this.prefabCacheMap = datamap;
        }

        /// <summary>
        /// 设置资源空闲时释放时间
        /// </summary>
        public void SetReleaseTimeAfterFree(float t)
        {
            this.defaultReleaseAfterFree = t;
            if (poolstrategy != null)
                this.ReleaseAfterFree = poolstrategy.PrefabReleaseAfterFree;
            else
                this.ReleaseAfterFree = t;
        }
        /// <summary>
        /// 资源计数+1
        /// </summary>
        public void Retain()
        {
            this.ReferenceCount++;
            if (LogSystem.IsLogAct(LogActType.PrefabReferenceCount))
            {
                logger.LogInfo(string.Format("Retain {0} {1}", this.AssetID.ToString(), ReferenceCount));
            }
        }
        /// <summary>
        /// 释放引用计算-1
        /// </summary>
        public void Release()
        {
            this.ReferenceCount--;
            lastFreeTime = Time.time;
            if(this.ReferenceCount < 0)
            {
                Debug.LogErrorFormat("[PrefabCache] referenceCount is error {0} {1} {2}", lastFreeTime, instancePrefab.name, ReferenceCount);
            }
            if (LogSystem.IsLogAct(LogActType.PrefabReferenceCount))
            {
                logger.LogInfo(string.Format("Release {0} {1}", this.AssetID.ToString(), ReferenceCount));
            }

        }

        public void Load(AssetID assetID)
        {
            Scheduler.RunCoroutine(AsyncLoadPrefab(assetID));
        }

        public bool IsNotNull()
        {
            return this.instancePrefab != null;
        }

        public GameObject GetObject()
        {
            return this.instancePrefab;
        }

        private IEnumerator AsyncLoadPrefab(AssetID assetid)
        {
            WaitLoadObject wait = null;
            if (IsSync)
            {
                wait = AssetManager.LoadObjectSync(assetid, typeof(GameObject));
            }
            else
            {
                if (LogSystem.IsLogAct(LogActType.AsyncLoadPrefab))
                {
                    logger.LogInfo(string.Format("AsyncLoadPrefab {0}", this.AssetID.ToString()));
                }
                wait = AssetManager.LoadObject(assetid, typeof(GameObject));
            }

            yield return wait;
            if (!string.IsNullOrEmpty(wait.Error))
            {
                this.Error = wait.Error;
                yield break;
            }

            instancePrefab = (wait.GetObject() as GameObject);
            if(instancePrefab == null)
            {
                this.Error = string.Format("This asset: {0} is not a GameObject", assetid);
                yield break;
            }

            if (LogSystem.IsLogAct(LogActType.AsyncLoadPrefabComplete))
            {
                logger.LogInfo(string.Format("AsyncLoadPrefabComplete {0}", this.AssetID.ToString()));
            }
            this.poolstrategy = instancePrefab.GetComponent<PoolStrategy>();
            if(this.poolstrategy != null)
            {
                this.ReleaseAfterFree = this.poolstrategy.PrefabReleaseAfterFree;
            }else
            {
                this.ReleaseAfterFree = this.defaultReleaseAfterFree;
            }
            if (this.prefabCacheMap.ContainsKey(instancePrefab))
            {
                logger.LogWarning("The prefab {0} has been loaded.",assetid);
                this.prefabCacheMap[instancePrefab] = this;
            }else
            {
                this.prefabCacheMap.Add(instancePrefab, this);
            }
            yield break;
        }



    }
}

